package com.it.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.it.constant.UserConstant;
import com.it.model.entity.User;
import com.it.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 提前进行缓存预热,写个定时任务,针对用户,提升第一次访问页面时数据加载的性能
 */
@Component
@Slf4j
public class PreRefreshCache {

    private final String redissonLockKey = "redisson:lock";

    @Resource
    private UserService userService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 先指定一个重要用户的主键id集合,优先加载这些用户的数据进行预热
     */
    private List<Long> vipUserList = Arrays.asList(1L);

    private Long pageNum = 1L;
    private Long pageSize = 20L;


    /**
     * 每天晚上23点59分进行定时预热
     */
    // TODO 后期再完善定时任务时长
    @Scheduled(cron = "0 59 23 * * *")
    public void doPreRefreshRecommendCache() {

        RLock redissonClientLock = redissonClient.getLock(redissonLockKey);

        try {

            /**
             *  // 不设 leaseTime (锁过期时间),默认为-1,则会开启看门狗机制,当业务还没执行完,会进行锁过期续命
             *  // 使用tryLock不会阻塞其他服务器的线程
             *  // 如果某个服务器线程抢到锁返回true,继续执行预热缓存逻辑
             *  // 如果某个服务器抢锁成功返回false,则不执行预热缓存的业务代码
             */
            if (redissonClientLock.tryLock()) {
                System.out.println(Thread.currentThread().getId() + "线程已经抢到redisson锁...准备执行缓存预热逻辑...");
                System.out.println("已执行定时任务...");
                // 遍历这些VIp用户
                for (Long vipUID : vipUserList) {
                    String redisKey = String.format(UserConstant.Ahead_User_Recommend_Str + ":%s", vipUID);

                    ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();

                    // 1. 先从数据库中查数据
                    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();

                    Page<User> usersPage = new Page<>(pageNum, pageSize);

                    userService.page(usersPage, userQueryWrapper);

                    // 2. 再向redis中插入数据
                    try {
                        // 设置过期时间为 1天 (24小时)
                        stringObjectValueOperations.set(redisKey, usersPage, 24, TimeUnit.HOURS);  // , 30000, TimeUnit.MICROSECONDS
                    } catch (Exception e) {
                        log.error("redis缓存设置用户信息失败...");
                    }
                }
            } else {
                System.out.println(Thread.currentThread().getId() + "获取锁失败...");
            }
        } catch (Exception e) {
            log.error("doCacheRecommendUser error", e);
        } finally {
            System.out.println(Thread.currentThread().getId() + "线程准备释放redisson锁...");
            redissonClientLock.unlock();
        }


    }


}
